#!/bin/bash
# - vt_name
# - vt_suffix
# - vt_reference
# - vt_type
# - vt_guest_os
# - sub_module
## - vt_machine_type
## - vt_no_filter
## - vt_only_filter
## - vt_env_type
## - no_filter
## - only_filter
## - vt_qemu_bin
## - vt_qemu_dst_bin
## - vt_extra_params
## - job_timeout

. $LKP_SRC/lib/debug.sh
. $LKP_SRC/lib/env.sh
. $LKP_SRC/lib/upload.sh
. $LKP_SRC/lib/job.sh

images_aarch64=("openEuler-20.03-aarch64.qcow2" "openEuler-20.09-aarch64.qcow2")
images_x86_64=("openEuler-20.03-x86_64.qcow2" "openEuler-20.09-x86_64.qcow2")
file_server="${image_server}/initrd/qemu-image"
images_path="/var/lib/avocado/data/avocado-vt/images/"
images_partition="/dev/sda"

default_dns_address="223.5.5.5"    # The default DNS address for compass-ci
dns_address=${dns_address:-$default_dns_address}
default_pwd="Helloman@12#$"
node_pwd=${node_pwd:-$default_pwd}

libvirt_base_cfg="/var/lib/avocado/data/avocado-vt/backends/libvirt/cfg/base.cfg"
bash_prompt="/root/.shell/bash_prompt"

set_up() {
    iptables -F

    if ! brctl show virbr0 > /dev/null; then
        log_cmd systemctl restart libvirtd ||systemctl unmask libvirtd && systemctl restart libvirtd
    fi
    ps -fC qemu-img | grep qemu-img && log_cmd killall -9 qemu-img
    log_cmd targetcli clearconfig confirm=True

    # virttest qemu path
    if ! ls /usr/bin/qemu-kvm > /dev/null; then
        # upstream qemu path
        if [ -e /usr/libexec/qemu-kvm ]; then
            log_cmd ln -s /usr/libexec/qemu-kvm /usr/bin/qemu-kvm
        fi
    fi

    if [ "$vt_type" = "libvirt" ]; then
        local vt_xml=$LKP_SRC/virttest/avocado-vt-vm1-$arch.xml
        if [ "$arch" = "aarch64" ]; then
            case "$vt_guest_os" in
                *openEuler.20.03) log_cmd sed -i "s/[^/]*\.qcow2/openEuler-20.03-aarch64.qcow2/" $vt_xml ;;
                *openEuler.20.09) log_cmd sed -i "s/[^/]*\.qcow2/openEuler-20.09-aarch64.qcow2/" $vt_xml ;;
                *               ) log_cmd sed -i "s/[^/]*\.qcow2/openEuler-20.03-aarch64.qcow2/" $vt_xml ;;
            esac

            # upstream edk2 path
            if [ ! -e /usr/share/edk2/aarch64/QEMU_EFI-pflash.raw ]; then
                # Fedora/CentOS/Redhat edk (aavmf) path
                if [ -e /usr/share/AAVMF/AAVMF_CODE.fd ]; then
                    log_cmd sed -i "s|/usr/share/edk2/aarch64/QEMU_EFI-pflash.raw|/usr/share/AAVMF/AAVMF_CODE.fd|" $vt_xml
                fi
            fi
        fi
        if [ "$arch" = "x86_64" ]; then
            case "$vt_guest_os" in
                *openEuler.20.03) log_cmd sed -i "s/[^/]*\.qcow2/openEuler-20.03-x86_64.qcow2/" $vt_xml ;;
                *openEuler.20.09) log_cmd sed -i "s/[^/]*\.qcow2/openEuler-20.09-x86_64.qcow2/" $vt_xml ;;
                *               ) log_cmd sed -i "s/[^/]*\.qcow2/openEuler-20.03-x86_64.qcow2/" $vt_xml ;;
            esac
        fi
        log_cmd virsh define $vt_xml
    fi
}

tear_down() {
    local errno=$1

    # collect aexpect (eg: serial console) logs
    ls -d /tmp/aexpect_* | while read aexpect; do
        case "$(awk '$5=="command:"{print;exit}' $aexpect/server-log | sed "s/^.*command: *//")" in
            ssh*          ) aexpect_errno=$errno; command=ssh     ;;
            scp*          ) aexpect_errno=$errno; command=scp     ;;
            tshark*       ) aexpect_errno=0     ; command=tshark  ;;
            tcpdump*      ) aexpect_errno=0     ; command=tcpdump ;;
            virsh*console*) aexpect_errno=x     ; command=console ;;
            *             ) aexpect_errno=0     ; command=others  ;;
        esac
        if [ "$errno" = "$aexpect_errno" ]; then
            rm -rf "$aexpect"
            continue
        fi
        date=$(ls -lrt --full-time "$aexpect" | awk 'NF>=9{print$6,$7,$8;exit}' | sed 's/^\(.\{19\}\).*$/\1/;s/[:-]//g;s/ /-/g')
        ls "$aexpect" | while read file; do
            if [ ! -s "$aexpect/$file" ]; then
                rm -rf "$aexpect/$file"
            fi
        done
        if ! mv "$aexpect" "$logs_dir/latest/sysinfo/post/aexpect-$command-$date-${aexpect##*_}"; then
            rm -rf "$aexpect"
        fi
    done

    if [ "$errno" != "0" ]; then
        ps -fC qemu-img qemu-kvm aexpect-helper && killall qemu-img qemu-kvm aexpect-helper
        local pids=$(ps -ef | awk '$8!="awk"&&/avocado.*run/{print$2}')
        sleep 1
        killall -9 qemu-img qemu-kvm aexpect-helper
        [ -n "$pids" ] && kill -9 $pids

        ps aux > "$logs_dir/latest/sysinfo/post/ps_aux"
        ps -e f > "$logs_dir/latest/sysinfo/post/ps-e_f"
        rpm -qa | sort > "$logs_dir/latest/sysinfo/post/rpm-qa"
        tail -n 2048 /var/log/messages > "$logs_dir/latest/sysinfo/post/tail_n_2048_var_log_messages"
        tar -zcvf "$logs_dir/latest/sysinfo/post/var_log_libvirt.tar.gz" /var/log/libvirt
    fi

    if [ "$vt_type" = "libvirt" ]; then
        virsh list --all | awk '/shut off$/{print$2}' | while read vm; do
            if ! log_cmd virsh undefine $vm --nvram; then
                log_cmd virsh undefine $vm
            fi
        done
    fi

    return $errno
}

run_test() {
    (
    set_up
    # used to collect aexpect (eg: serial console) logs
    export AEXPECT_DEBUG=on
    log_cmd "$@"
    tear_down $?
    return $?
    )
}

config_virttest() {
    local_ip=$1
    remote_ip=$2
    local_pwd=$3
    local_dir=$images_path
    remote_dir=/Images/virt-test/remote_dir
    export_options="rw,sync,no_root_squash"
    mkdir -p $remote_dir

    cp -a $libvirt_base_cfg $libvirt_base_cfg.bak_by_CI

    [ -f /root/.ssh/id_rsa ] && log_cmd rm -rf /root/.ssh/id_rsa*
    
    sed -i "/^# storage_type=/i firewall_to_permit_nfs = no" $libvirt_base_cfg
    sed -i "/^# storage_type =/i storage_type = nfs" $libvirt_base_cfg
    sed -i "/^# storage_type =/i setup_remote_nfs = yes" $libvirt_base_cfg
    sed -i "/^# storage_type =/i setup_local_nfs = yes" $libvirt_base_cfg
    sed -i "/^# storage_type =/i nfs_server_ip = \"$local_ip\"" $libvirt_base_cfg
    sed -i "/^# storage_type =/i server_ip = \"$local_ip\"" $libvirt_base_cfg
    sed -i "/^# storage_type =/i client_ip = \"$remote_ip\"" $libvirt_base_cfg
    sed -i "/^# storage_type =/i nfs_client_ip = \"$remote_ip\"" $libvirt_base_cfg
    sed -i "/^# storage_type =/i nfs_server_pwd = \"$local_pwd\"" $libvirt_base_cfg
    sed -i "/^# storage_type =/i nfs_client_pwd = \"$local_pwd\"" $libvirt_base_cfg
    sed -i "/^# storage_type =/i nfs_mount_dir = \"$remote_dir\"" $libvirt_base_cfg
    sed -i "/^# storage_type =/i nfs_mount_src = \"$local_dir\"" $libvirt_base_cfg
    sed -i "/^# storage_type =/i export_options = \"$export_options\"" $libvirt_base_cfg
    sed -i "/^# storage_type =/i export_dir = \"$local_dir\"" $libvirt_base_cfg
    sed -i "/^# storage_type =/i remote_nfs_mount = yes" $libvirt_base_cfg

    sed -i "/^local_ip =/c local_ip = \"$local_ip\"" $libvirt_base_cfg
    sed -i "/^local_pwd =/c local_pwd = \"$local_pwd\"" $libvirt_base_cfg
    sed -i "/^remote_ip =/c remote_ip = \"$remote_ip\"" $libvirt_base_cfg
    sed -i "/^migrateion_setup =/c migration_setup = \"yes\"" $libvirt_base_cfg

    sed -i 's/^migrate_source_host = ENTER.YOUR.SOURCE.EXAMPLE.COM/# &/' $libvirt_base_cfg
    sed -i 's/^migrate_source_pwd = PASSWORD.SOURCE.EXAMPLE/# &/' $libvirt_base_cfg
    sed -i 's/^# \(migrate_source_host = "${local_ip}"\)/\1/' $libvirt_base_cfg
    sed -i 's/^# \(migrate_source_pwd = "${local_pwd}"\)/\1/' $libvirt_base_cfg

    sed -i 's/^migrate_dest_host = ENTER.YOUR.DEST.EXAMPLE.COM/# &/' $libvirt_base_cfg
    sed -i 's/^migrate_dest_pwd = PASSWORD.DEST.EXAMPLE/# &/' $libvirt_base_cfg
    sed -i 's/^# \(migrate_dest_host = "${remote_ip}"\)/\1/' $libvirt_base_cfg
    sed -i 's/^# \(migrate_dest_pwd = "${remote_pwd}"\)/\1/' $libvirt_base_cfg
}

config_qemu_san() {
    profile=/etc/profile
    lsan_file=/home/suppfile
    echo "export LSAN_OPTIONS=suppressions=$lsan_file" >> $profile
    echo "export ASAN_OPTIONS=detect_leaks=1:halt_on_error=0:log_path=/home/sanlog/:log_exe_name=1:disable_coredump=0:fast_unwind_on_malloc=0" >> $profile
    source $profile
    mkdir -p /home/sanlog
    chmod 777 /home/sanlog/
    touch $lsan_file
    yum install -y libasan
}  

prepare_qemu_san() {
    if [ x$qemu_san == x"enable" ] && [ ! -d /virttest_san_rpm_bak ]; then
        [ -d /virttest_san_rpm ] || die "Makepkg is short of san packages and needs to be remakepkg with qemu_san/libvirt_san enable"
        rpm -Uvh /virttest_san_rpm/*.rpm --force || exit $?
        config_qemu_san
        mv /virttest_san_rpm /virttest_san_rpm_bak
    fi
}

prepare_upstream_qemu_san() {
    upstream_qemu_path="/usr/libexec/upstream_qemu"
    if [ x$upstream_qemu_san == x"enable" ] && [ ! -h /usr/libexec/qemu-kvm ] && [ x$qemu_san != x"enable" ]; then
        [ -d $upstream_qemu_path ] || die "Makepkg is short of upstream qemu san file and needs to be remakepkg with upstream_qemu_san enable"
        ln -sf $upstream_qemu_path/qemu-system-$arch /usr/libexec/qemu-kvm
        ln -sf $upstream_qemu_path/qemu-img $(which qemu-img) 
        config_qemu_san
    fi
}

# upload sanlog
upload_sanlog() {
    if [ x$qemu_san == x"enable" -o x$upstream_qemu_san == x"enable" ]; then
        echo "$vt_reference" > "/home/sanlog/date.log"
        upload_files -t results "/home/sanlog"
        rm -rf /home/sanlog/*
    fi
}

# config bash prompt. The shell prompt that indicates a successfull login 
config_bash_prompt() {
    if [ -f $bash_prompt -a ! -e $bash_prompt.bak_by_CI ]; then
        cp -a $bash_prompt $bash_prompt.bak_by_CI
        sed -i '/^PS1=/c PS1=\"[\\u@\\h \\W]# \"' $bash_prompt
        source  $bash_prompt
    fi
}

config_umask() {
    if [ ! -e /etc/bashrc.bak_by_CI ]; then
        cp -a /etc/bashrc /etc/bashrc.bak_by_CI
        sed -i '/^umask /c umask 0022' /etc/bashrc
    fi
}

config_qemu_conf() {
    if [ ! -e /etc/libvirt/qemu.conf.bak_by_CI ];then
        cp /etc/libvirt/qemu.conf /etc/libvirt/qemu.conf.bak_by_CI
        sed -i 's/^#user/user/;s/^#group/group/' /etc/libvirt/qemu.conf
        if [ x$upstream_qemu_san == x"enable" ] && [ x$qemu_san != x"enable" ]; then
            sed -i 's/\#seccomp_sandbox = 1/seccomp_sandbox = 0/g' /etc/libvirt/qemu.conf
        fi
    fi
}

config_dns() {
    if [ ! -f /etc/resolv.conf ]; then
        echo "nameserver $dns_address" >> /etc/resolv.conf
    fi
}

unmask_service() {
    if systemctl status network.target |grep "is masked" || systemctl status libvirtd |grep "is masked"; then
        log_cmd systemctl restart nfs ||systemctl unmask network.target
        log_cmd systemctl restart libvirtd ||systemctl unmask libvirtd && systemctl restart libvirtd
    fi
}

# avocado-vt requirements check
check_avocado_req() {
    pip3 list | grep simplejson || pip3 install simplejson
    pip3 list | grep psutil || pip3 install psutil
    pip3 list | grep netifaces || pip3 install netifaces
}

# prepare cmd "service", can not install initscripts via yum when os_mount=initramfs
prepare_for_initramfs() {
    if [ $os_mount == "initramfs" ] && [ ! -f /usr/sbin/service ];then
        rpm_tmp="/root/rpm_tmp"
        yum install -y initscripts --downloadonly --downloaddir $rpm_tmp
        rpm2cpio $rpm_tmp/initscripts*.rpm | cpio -idv ./usr/sbin/service
        cp ./usr/sbin/service /usr/sbin/
        rm -rf ${rpm_tmp:?}
    fi
}

prepare_for_2hosts() {
    # if it is 2hostos remote env, exit 0 without run any test
    if [ $vt_reference == "remote_deploy" ]; then
        exit 0
    fi

    # config virttest for 2hosts local env
    if [ x$env_type == x"2hosts" -a ! -e $libvirt_base_cfg.bak_by_CI ];then
        config_virttest $server $client $node_pwd
    fi
}

# config tmp dir
config_tmpdir() {
    umount /var/tmp > /dev/null  2>&1
    rm -rf ${images_path:?}/tmp
    mkdir -p ${images_path}/tmp
    [ -d /var/tmp ] || mkdir -p /var/tmp
    export TMP="/var/tmp"
    mount --bind ${images_path}/tmp /var/tmp
}

# config images file system
config_image_filesystem() {
    if [ $os_mount == "cifs" -o $os_mount == "nfs" -o $os_mount == "initramfs" ]; then
        mount $images_partition $images_path || echo y | mkfs.ext4 $images_partition && mount $images_partition $images_path
    fi
    config_tmpdir
}

# download images
download_images() {
    if [ "$arch" = "aarch64" ];then
        images=${images_aarch64[@]}
    else
        images=${images_x86_64[@]}
    fi

    for image in ${images[@]};do
        if [ ! -f $images_path/$image.succ ]; then
            wget -dq $file_server/$image -O $images_path/$image
            wget $file_server/$image.succ -O $images_path/$image.succ
        else
            wget $file_server/$image.succ -O $images_path/tmp.succ
            diff $images_path/tmp.succ $images_path/$image.succ
            if [ $? -ne 0 ]; then
                rm -rf $images_path/$image.backup
                wget -dq $file_server/$image -O $images_path/$image
                wget $file_server/$image.succ -O $images_path/$image.succ
            fi
        fi
    done
}

prepare_avocado_params() {
    conf_file=$(find /usr/local/lib/ -name "avocado.conf")
    [ -e "$conf_file" ] || die "Missing config file avocado.conf"

    logs_dir=$(awk -F= '/logs_dir/{print$2}' "$conf_file" | xargs | sed "s#^~#$(cd ~; pwd)#")
    [ -z "$logs_dir" ] && die "Not found logs_dir in avocado.conf"

    if [ "$arch" = "aarch64" ]; then
        [ "$vt_machine_type" = "q35" ] && echo "$arch vt_machine_type=$vt_machine_type, skip run" && exit 0
    else
        [ "$vt_machine_type" = "arm64-pci" ] && echo "$arch vt_machine_type=$vt_machine_type, skip run" && exit 0
    fi

    [ -z "$vt_name" ] && vt_name=$vt_reference
    [ -z "$vt_type" ] && vt_type="libvirt"
    [ -z "$vt_guest_os" ] && vt_guest_os="Guest.Linux.openEuler.20.03"
    [ -z "$vt_machine_type" ] &&
        if [ "$arch" = "aarch64" ]; then
            vt_machine_type="arm64-pci"
        else
            vt_machine_type="q35"
        fi
    [ -z "$job_timeout" ] && job_timeout=600

    base_params=(--vt-type "$vt_type")
    base_params+=(--vt-guest-os "$vt_guest_os")
    base_params+=(--vt-machine-type "$vt_machine_type")

    extra_params=()
    [ -n "$vt_no_filter" ] && extra_params+=(--vt-no-filter "$vt_no_filter")
    [ -n "$vt_only_filter" ] && extra_params+=(--vt-only-filter "$vt_only_filter")

    vt_tests=$(avocado list "${vt_reference:?}" "${base_params[@]}" "${extra_params[@]}")
    [ $? -eq 0 ] || die "Failed to exec: avocado list ${vt_reference:?} ${base_params[@]} ${extra_params[@]}"

    vt_tests=$(echo "$vt_tests" | awk '{print$2}')
    for _no_filter in $no_filter; do
        vt_tests=$(echo "$vt_tests" | grep -Ev "$_no_filter") || break
    done
    for _only_filter in $only_filter; do
        vt_tests=$(echo "$vt_tests" | grep -E "$_only_filter") || break
    done
    [ -z "$vt_tests" ] && die "Not found tests: avocado list ${vt_reference:?} ${base_params[@]} ${extra_params[@]}"
}

run_reference_test() {
    test_ok=0
    test_all=0
    for vt_test in $vt_tests; do
        run_test avocado run "${vt_test:?}" "${base_params[@]}" --job-timeout "$job_timeout" 2>&1
        [ $? -eq 0 ] && ((test_ok++))
        ((test_all++))
    done

    echo "$vt_name.nr_pass: $test_ok"
    echo "$vt_name.nr_fail: $(($test_all-$test_ok))"
    echo "$vt_name.nr_total: $test_all"
    echo "$vt_name.pass_rate: $(($test_ok*100/$test_all))"

    mkdir /home/$vt_reference
    mv ${logs_dir}/* /home/$vt_reference
    upload_files -t results /home/$vt_reference
    rm -rf /home/${vt_reference:?}
}

prepare_qemu_san
prepare_upstream_qemu_san
config_bash_prompt
config_umask
config_qemu_conf
config_dns
unmask_service
check_avocado_req
prepare_for_initramfs
prepare_for_2hosts

prepare_avocado_params
config_image_filesystem
download_images
run_reference_test
upload_sanlog

run_target_stats $(basename $0)
